package com.eternal.export;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import androidx.lifecycle.Observer;
import androidx.paging.PagedList;

import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;

import androidx.annotation.Nullable;
import android.os.Bundle;
import androidx.annotation.WorkerThread;
import androidx.core.content.FileProvider;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.eternal.base.concat.DeviceInfo;
import com.eternal.base.concat.DeviceName;
import com.eternal.base.concat.TmpHum;
import com.eternal.base.data.RepositoryInjection;
import com.eternal.base.global.ActivityEvent;
import com.eternal.base.router.RouterActivityPath;
import com.eternal.export.databinding.ActivityExportBinding;
import com.eternal.framework.binding.command.BindingAction;
import com.eternal.framework.binding.command.BindingConsumer;
import com.eternal.framework.bus.Messenger;
import com.eternal.framework.component.BaseActivity;
import com.eternal.framework.utils.KLog;
import com.eternal.framework.utils.PathUtils;
import com.eternal.widget.guqiang.ProgressToolbar;
import com.google.common.collect.Lists;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import io.reactivex.Completable;
import io.reactivex.CompletableEmitter;
import io.reactivex.CompletableOnSubscribe;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

@Route(path = RouterActivityPath.Export.PAGE_EXPORT)
public class ExportActivity extends BaseActivity<ActivityExportBinding, ExportModel> {
    public static final String EXPORT="EXPORT";
    public static final String CHECK_TIME="check time";
    private CSVUtil util;
    private PagedList<TmpHum> data;
    private int position;
    private SimpleDateFormat format;
    private Calendar calendar;
    private Disposable exportDispose,nameRefresh;
    private MaterialDialog dialog;
    private String name,mac;
    private byte port;
    private ValueAnimator animator;
    private ProgressToolbar toolbar;

    private static final String[] NARMAL_PHONE = {"com.android.email", "com.android.email.activity.MessageCompose"};
    private static final String[] MIUI_PHONE = {"com.android.email", "com.kingsoft.mail.compose.ComposeActivity"};
    private static final String[] SAMSUNG_PHONE = {"com.samsung.android.email.provider", "com.samsung.android.email.composer.activity.MessageCompose"};

    @Override
    public int initContentView(Bundle savedInstanceState) {
        return R.layout.activity_export;
    }

    @Override
    public int initVariableId() {
        return BR.model;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        init();

    }

    @SuppressLint("CheckResult")
    private void init(){
        calendar=Calendar.getInstance();
        format=new SimpleDateFormat("M/d/yyyy HH:mm:ss", Locale.ENGLISH);
        Intent intent=getIntent();
        mac = intent.getStringExtra(ActivityEvent.DEVICE_MAC);
        port = intent.getByteExtra(ActivityEvent.DEVICE_PORT,(byte)0x0);
        viewModel.init(RepositoryInjection.providerHistoryRepository(),mac,port
                , intent.getStringExtra(ActivityEvent.DEVICE_TIME))
                .compose(this.<ArrayList<String>>bindToLifecycle())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<ArrayList<String>>() {
                    @Override
                    public void accept(ArrayList<String> strings) throws Exception {
                        binding.wvStartYear.setData(strings);
                        binding.wvEndYear.setData(strings);
                        binding.wvStartDay.setData(createDay(Integer.parseInt(strings.get(strings.size()-1)),
                                binding.wvStartMonth.getSelected()));
                        binding.wvEndDay.setData(createDay(Integer.parseInt(strings.get(0)),
                                binding.wvEndMonth.getSelected()));
                        initWheel();
                        bindMonthEvent();
                    }
                });
        initView();
        bindEvent();
        registerMessage();
        refreshName();
    }

    private void initWheel(){
        binding.wvStartYear.setDefault(viewModel.startYearNum-1970);
        binding.wvEndYear.setDefault(viewModel.endYearNum-1970);
        binding.wvStartDay.setDefault(viewModel.startDayNum-1);
        binding.wvEndDay.setDefault(viewModel.endDayNum-1);
        binding.wvStartMonth.setDefault(viewModel.startMonthNum.getValue());
        binding.wvEndMonth.setDefault(viewModel.endMonthNum.getValue());
        binding.wvStartHour.setDefault(viewModel.startHourNum-1);
        binding.wvEndHour.setDefault(viewModel.endHourNum-1);
        binding.wvStartMin.setDefault(viewModel.startMinuteNum);
        binding.wvEndMin.setDefault(viewModel.endMinuteNum);
        binding.wvStartAmPm.setDefault(viewModel.startIsAm?0:1);
        binding.wvEndAmPm.setDefault(viewModel.endIsAm?0:1);
    }

    @Override
    protected void onDestroy() {
        unregisterMessage();
        if (nameRefresh != null) {
            nameRefresh.dispose();
            nameRefresh = null;
        }
        super.onDestroy();
    }

    private void initView(){
        ArrayList<String> ap = Lists.newArrayList("AM", "PM");
        ArrayList<String> hours = viewModel.createNumber(1, 12);
        ArrayList<String> minutes = Lists.newArrayListWithCapacity(60);
        for (int i = 0; i < 60; i++) {
            minutes.add(String.format(Locale.ENGLISH, "%02d", i));
        }
        binding.wvStartHour.setData(hours);
        binding.wvEndHour.setData(hours);
        binding.wvStartAmPm.setData(ap);
        binding.wvEndAmPm.setData(ap);
        binding.wvStartMin.setData(minutes);
        binding.wvEndMin.setData(minutes);
        binding.wvStartMonth.setData(viewModel.months);
        binding.wvEndMonth.setData(viewModel.months);

        toolbar = findViewById(R.id.toolbar);
        toolbar.getBack().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewModel.onBack.execute();
            }
        });
    }

    private void registerMessage(){
        Messenger.getDefault().register(this, EXPORT, new BindingAction() {
            @Override
            public void call() {
                requestPermissionAndExport();
            }
        });
        Messenger.getDefault().register(this, CHECK_TIME, Boolean.class, new BindingConsumer<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                checkTime(aBoolean);
            }
        });
    }

    private void unregisterMessage(){
        Messenger.getDefault().unregister(this);
    }

    @SuppressLint("CheckResult")
    private void requestPermissionAndExport(){
        exportDispose= viewModel.getDeviceInfo()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(new Function<DeviceInfo, ObservableSource<long[]>>() {
                    @Override
                    public ObservableSource<long[]> apply(DeviceInfo deviceInfo) {
                        return export(deviceInfo.name, deviceInfo.isDegree);
                    }
                })
                .observeOn(Schedulers.io())
                .subscribe(new Consumer<long[]>() {
                    @Override
                    public void accept(long[] ints) {
                        viewModel.export(ints[0], ints[1])
                                .observe(ExportActivity.this, new Observer<PagedList<TmpHum>>() {
                                    @Override
                                    public void onChanged(@Nullable PagedList<TmpHum> tmpHums) {
                                        if (tmpHums == null || tmpHums.isEmpty()) {
                                            finishSave();
                                        } else if (data == null) {
                                            data = tmpHums;
                                            data.addWeakCallback(null, callback);
                                            try {
                                                save();
                                            } catch (IOException e) {
                                                KLog.e("write file err");
                                            }
                                        }
                                    }
                                });
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        throwable.printStackTrace();
                        KLog.e(throwable);
                    }
                });
    }

    private void bindEvent(){
        viewModel.startEdit.observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {
                if(aBoolean){
                    binding.tvStartTimeEdit.setText("OK");
                    binding.elStartTime.expand();
                }else{
                    binding.tvStartTimeEdit.setText("EDIT");
                    binding.elStartTime.collapse();
                }
            }
        });

        viewModel.endEdit.observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {
                if(aBoolean){
                    binding.tvEndTimeEdit.setText("OK");
                    binding.elEndTime.expand();
                }else{
                    binding.tvEndTimeEdit.setText("EDIT");
                    binding.elEndTime.collapse();
                }
            }
        });
    }

    private void bindMonthEvent(){
        viewModel.startMonthNum.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer integer) {
                ArrayList<String> temp=createDay(viewModel.startYearNum, integer);
                binding.wvStartDay.setData(temp);
                int index=viewModel.startDayNum-1;
                if(index>=temp.size()){
                    index=0;
                }
                binding.wvStartDay.setDefault(index);
                viewModel.startDayNum=Integer.parseInt(temp.get(index));
                viewModel.updateStartTime();
            }
        });

        viewModel.endMonthNum.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer integer) {
                ArrayList<String> temp=createDay(viewModel.endYearNum, integer);
                binding.wvEndDay.setData(temp);
                int index=viewModel.endDayNum-1;
                if(index>=temp.size()){
                    index=0;
                }
                binding.wvEndDay.setDefault(index);
                viewModel.endDayNum=Integer.parseInt(temp.get(index));
                viewModel.updateEndTime();
            }
        });
    }

    @SuppressLint("CheckResult")
    private void refreshName() {
        if (nameRefresh == null || nameRefresh.isDisposed()) {
            nameRefresh = RepositoryInjection.providerDeviceRepository()
                    .getDeviceName(mac, port)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<DeviceName>() {
                        @Override
                        public void accept(DeviceName deviceName) {
                            if (deviceName != null) {
                                toolbar.setSubtitle(deviceName.name);
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) {
                            KLog.e(throwable);
                        }
                    });
        }
    }

    @SuppressLint("CheckResult")
    private Observable<long[]> export(String name, final boolean isDegree){
        this.name=name;
        final long start=getStartTime();
        final long end=getEndTime();
        final MaterialDialog.Builder builder;
        final View view;
        int current= (int) (System.currentTimeMillis()/60000);
        if(start/60>current){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("Start date or time cannot exceed current date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }else if(end/60>current){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("End date or time cannot exceed current date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }else if(start>end){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("Start date or time cannot exceed end date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }else{
            view=LayoutInflater.from(this).inflate(R.layout.dialog_progress, null, false);
            TextView content=view.findViewById(R.id.tv_title);
            content.setText("Exporting data for Device ID "+name+"\n" +
                    "This process can sometimes take\n" +
                    "several minutes.");
            builder=new MaterialDialog.Builder(this)
                    .backgroundColor(0x00ffffff)
                    .customView(view, false);
            return Completable.create(new CompletableOnSubscribe() {
                @Override
                public void subscribe(CompletableEmitter emitter) throws Exception {
                    dialog=builder.build();
                    dialog.show();
                    startAnimator(view.findViewById(R.id.iv_progress));
                    dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                        @Override
                        public void onDismiss(DialogInterface dialog) {
                            if(data!=null&&callback!=null){
                                data.removeWeakCallback(callback);
                            }
                            data=null;
                            exportDispose.dispose();
                            animator.end();
                        }
                    });
                    emitter.onComplete();
                }
            }).compose(bindToLifecycle()).subscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(Schedulers.io())
                    .andThen(Observable.create(new ObservableOnSubscribe<long[]>() {
                        @Override
                        public void subscribe(ObservableEmitter<long[]> emitter) throws Exception {
                            util=CSVUtil.build(PathUtils.getInternalAppFilesPath()+"/AC INFINITY Data.csv");
                            util.writeElements("Device ID", ExportActivity.this.name);
                            util.writeElements("Export Time", "\"\"\""+format.format(new Date())+"\"\"\"");
                            util.writeElements("Sample Frequency", formatFrequency(viewModel.frequency));
                            util.writeElements("Start Time", "\"\"\""+format.format(new Date(start*1000L))+"\"\"\"");
                            util.writeElements("End Time", "\"\"\""+format.format(new Date(end*1000L))+"\"\"\"");
                            util.writeElements("Temperature Units", getUnit(isDegree));
                            util.writeLine("");
                            util.writeElements("Time", "Temperature "+getUnit(isDegree), "Relative Humidity");
                            position=0;
                            emitter.onNext(new long[]{start, end});
                            emitter.onComplete();
                        }
                    }));
        }
        return Completable.create(new CompletableOnSubscribe() {
            @Override
            public void subscribe(CompletableEmitter emitter) {
                builder.backgroundColor(0x00ffffff);
                dialog=builder.build();
                view.findViewById(R.id.tv_confirm).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog.dismiss();
                    }
                });
                dialog.show();
            }
        }).compose(bindToLifecycle()).subscribeOn(AndroidSchedulers.mainThread()).andThen(Observable.<long[]>empty());
    }

    private long getStartTime(){
        calendar.set(Calendar.YEAR, viewModel.startYearNum);
        calendar.set(Calendar.MONTH, viewModel.startMonthNum.getValue());
        calendar.set(Calendar.DAY_OF_MONTH, viewModel.startDayNum);
        if(viewModel.startIsAm){
            if(viewModel.startHourNum==12){
                calendar.set(Calendar.HOUR_OF_DAY, 0);
            }else{
                calendar.set(Calendar.HOUR_OF_DAY, viewModel.startHourNum);
            }
        }else{
            if(viewModel.startHourNum==12){
                calendar.set(Calendar.HOUR_OF_DAY, 12);
            }else{
                calendar.set(Calendar.HOUR_OF_DAY, viewModel.startHourNum+12);
            }
        }
        calendar.set(Calendar.MINUTE, viewModel.startMinuteNum);
        return calendar.getTimeInMillis()/1000;
    }

    private void startAnimator(final View view){
        animator=ValueAnimator.ofInt(0, 11);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                view.setRotation((int)animation.getAnimatedValue()*30);
            }
        });
        animator.setDuration(1000);
        animator.setRepeatCount(-1);
        animator.start();
    }

    private void checkTime(boolean isStart){
        final long start=getStartTime();
        final long end=getEndTime();
        MaterialDialog.Builder builder=null;
        View view=null;
        initWheel();
        int current= (int) (System.currentTimeMillis()/60000);
        if(start/60>current){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("Start date or time cannot exceed current date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }else if(end/60>current){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("End date or time cannot exceed current date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }else if(start>end){
            view= LayoutInflater.from(this).inflate(R.layout.dialog_tip_ok, null, false);
            TextView content=view.findViewById(R.id.tv_content);
            content.setText("Start date or time cannot exceed end date or time");
            builder=new MaterialDialog.Builder(this)
                    .customView(view, false);
        }
        if(builder!=null){
            if(isStart){
                viewModel.loadStart();
            }else{
                viewModel.loadEnd();
            }
            initWheel();
            builder.backgroundColor(0x00ffffff);
            dialog=builder.build();
            view.findViewById(R.id.tv_confirm).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.dismiss();
                }
            });
            dialog.show();
        }
    }

    private long getEndTime(){
        calendar.set(Calendar.YEAR, viewModel.endYearNum);
        calendar.set(Calendar.MONTH, viewModel.endMonthNum.getValue());
        calendar.set(Calendar.DAY_OF_MONTH, viewModel.endDayNum);
        if(viewModel.endIsAm){
            if(viewModel.endHourNum==12){
                calendar.set(Calendar.HOUR_OF_DAY, 0);
            }else{
                calendar.set(Calendar.HOUR_OF_DAY, viewModel.endHourNum);
            }
        }else{
            if(viewModel.endHourNum==12){
                calendar.set(Calendar.HOUR_OF_DAY, 12);
            }else{
                calendar.set(Calendar.HOUR_OF_DAY, viewModel.endHourNum+12);
            }
        }
        calendar.set(Calendar.MINUTE, viewModel.endMinuteNum);
        return calendar.getTimeInMillis()/1000;
    }

    private void finishSave(){
        try {
            util.sync();
            share();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            dialog.dismiss();
        }
    }

    private void share() {
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setData(Uri.parse("mailto:"));
        intent.putExtra(Intent.EXTRA_EMAIL, "");
        intent.putExtra(Intent.EXTRA_SUBJECT, "AC Infinity data");
        intent.setType("text/plain");
        Uri uri=FileProvider.getUriForFile(this,
                getPackageName()+".fileProvider", new File(PathUtils.getInternalAppFilesPath()+"/AC INFINITY Data.csv"));
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.putExtra(Intent.EXTRA_STREAM, uri);

//        if (getDeviceBrand().toUpperCase().contains("HONOR") || getDeviceBrand().toUpperCase().contains("HUAWEI") || getDeviceBrand().toUpperCase().contains("NUBIA")) {
//            intent.setClassName(NARMAL_PHONE[0], NARMAL_PHONE[1]);
//        } else if (getDeviceBrand().toUpperCase().contains("XIAOMI") || getDeviceBrand().toUpperCase().contains("XIAOMI")) {
//            intent.setClassName(MIUI_PHONE[0], MIUI_PHONE[1]);
//        } else if (getDeviceBrand().toUpperCase().contains("SAMSUNG")) {
//            intent.setClassName(SAMSUNG_PHONE[0], SAMSUNG_PHONE[1]);
//        }
        startActivity(Intent.createChooser(intent, "Share"));
    }

    private TmpHum getItem(int position){
        data.loadAround(position);
        return data.get(position);
    }

    @WorkerThread
    private void save() throws IOException {
        TmpHum temp;
        while(position<data.size()){
            temp=getItem(position);
            if(temp==null){
                break;
            }
            util.writeLine("");
            util.writeElements(format.format(new Date(temp.time*1000L)), formatValue(temp.getTmpValue()), formatValue(temp.hum));
            ++position;
        }
        if(position==data.size()){
            finishSave();
        }
    }

    private String formatValue(int value){
        return String.format(Locale.ENGLISH,"%.2f", value/100f);
    }

    private String formatFrequency(int frequency){
        if(frequency<60){
            return frequency+" MIN";
        }
        return frequency/60+" HRS";
    }

    private String getUnit(boolean isDegree){
        if(isDegree){
            return "C";
        }else{
            return "F";
        }
    }

    PagedList.Callback callback=new PagedList.Callback() {
        @Override
        public void onChanged(int position, int count) {
            try {
                save();
            } catch (IOException e) {
                KLog.e("write file err");
            }
        }

        @Override
        public void onInserted(int position, int count) {
            KLog.e("on insert");
        }

        @Override
        public void onRemoved(int position, int count) {
            KLog.e("on remove");
        }
    };

    private ArrayList<String> createDay(int year,int month){
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month);
        return viewModel.createNumber(1, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
    }

    @Override
    public void onBackPressed() {
        viewModel.onBack.execute();
    }

    public static String getDeviceBrand() {
        Log.e("--获取手机厂商--:", android.os.Build.BRAND);
        return android.os.Build.BRAND;
    }
}
